iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0

在上一篇文章中,我們介紹了 Rust 中高效能的 Web 框架 Actix。本篇文章,我們將探討 Rust 的另一個知名 Web 框架:Rocket,並對比 Actix 與 Rocket 在實際開發中的應用差異,幫助讀者更好地理解這兩個 Web 框架。


Rocket

Rocket 是一個專為 Rust 語言設計的 Web 框架,它以簡單易用、有方便的錯誤處理機制與靜態類型檢查機制。Rocket 是採用了同步編程模式,並透過巨集與強類型系統進行靜態檢查,這讓我們在編譯時就能檢測大多數錯誤,而非等到運行時才發現問題。這種靜態檢查對於增強程式的健壯性有極大幫助,尤其是在需要高度可靠的應用場景中。


一、Rocket 的簡單範例

我們首先來看看如何使用 Rocket 建立一個簡單的 Web 應用程式:

1. 建立專案並引入套件

使用以下命令建立新專案並安裝 Rocket 套件:

cargo new rocket-web
cd rocket-web

然後在 Cargo.toml 文件中添加 Rocket 套件:

[dependencies]
rocket = "0.5.0-rc.1"

接下來執行 cargo build,來下載並編譯 Rocket 框架。


2. 編寫主程式

我們可以編寫一個簡單的 main.rs,來建立一個基本的 HTTP 伺服器,回傳 "Hello from Rocket!" 給用戶:

// 使用 #[macro_use] 將 Rocket 巨集引入到程式中,使我們可以使用路由相關的巨集
#[macro_use] extern crate rocket; 

// 定義一個路由,處理 GET 請求,當使用者訪問 "/" 路徑時,會觸發這個函數
#[get("/")] 
// 定義一個名為 index 的函數,它會回傳一個靜態字串
fn index() -> &'static str { 
    // 當使用者訪問 "/" 路徑時,回傳這個字串作為 HTTP 回應
    "Hello from Rocket!" 
}

// 使用 #[launch] 巨集,這標記該函數為 Rocket 應用的啟動點
#[launch] 
// 定義一個名為 rocket 的函數,回傳 Rocket 伺服器的設定
fn rocket() -> _ { 
    // 建立並設定一個新的 Rocket 伺服器實例
    rocket::build() 
        // 將 "/" 路徑掛載到 index 路由,當訪問 "/" 時執行 index 函數
        .mount("/", routes![index])
}

範例說明

  • #[macro_use] extern crate rocket;:這個語法是將 Rocket 巨集引入到作用域中,讓我們能夠使用如 #[get] 等路由註解來定義 HTTP 請求。
  • #[get("/")]:這是 Rocket 的路由註解,指定該函數處理 GET 請求,並定義請求的路徑(此處是根 / 路徑)。
  • fn index():這個函數會處理來自 / 路徑的請求,並回傳一個靜態字串作為回應。
  • #[launch]:這是 Rocket 的一個屬性,用來標記應用的啟動函數。
  • rocket::build():使用 Rocket 建立一個新的伺服器實例,並通過 mount 方法將路由附加到伺服器上。

3. 執行並測試程式

當我們完成 main.rs 文件編寫後,可以使用以下命令來啟動 Rocket 伺服器:

cargo run

伺服器啟動後,您應該會看到以下類似的終端輸出:

Rocket has launched from http://127.0.0.1:8000

https://ithelp.ithome.com.tw/upload/images/20241008/20121176Kf825vCWl3.png

這表示 Rocket 伺服器已經啟動並運行在本機的 http://127.0.0.1:8000 上。現在打開瀏覽器並訪問該 URL,您將看到以下訊息:

Hello from Rocket!

這就是我們在 index 函數中定義的靜態回應內容,成功顯示在瀏覽器中。


二、使用 Tera 模板引擎來渲染 HTML

Rocket 不僅可以處理純文字回應,也能夠動態地渲染 HTML 頁面,這在需要動態生成內容或展示更複雜頁面時非常實用。Rocket 支援多種模板引擎,這裡我們將使用 Tera 模板引擎作為範例。

1. 引入 Tera 套件

首先,我們需要在 Cargo.toml 文件中添加 Tera 套件:

[dependencies]
rocket = "0.5.0-rc.1"
tera = "1.17"

設定完之後,執行以下指令來更新項目:

cargo build

這樣,我們就安裝好了 Rocket 和 Tera,用於後續的模板渲染。


2. 編寫 Tera 模板

接著,我們需要創建一個 templates 資料夾來存放 HTML 模板文件。在專案的根目錄下創建 templates/index.html 文件,內容如下:

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Rocket + Tera Example</title>
</head>
<body>
    <h1>Hello from Rocket using Tera!</h1>
    <p>Welcome, {{ name }}!</p>
</body>
</html>

這個模板將會顯示一個歡迎訊息,並動態展示傳遞進來的 name 參數,這個 index.html 所在的位置應該是在 templates 資料夾內。

rocket-web/
├── src/
│   └── main.rs
├── target/
├── templates/
│   └── index.html
├── .gitignore
├── Cargo.lock
└── Cargo.toml

網頁模板建立完成之後,我們接著要來設定 route 的部分。


3. 修改 main.rs 來渲染模板

現在,我們需要修改 main.rs 來渲染這個模板,以下是修改過的程式碼:

#[macro_use]
extern crate rocket;
use rocket::response::content::RawHtml;
use rocket::State;
use tera::{Context, Tera};

#[launch]
fn rocket() -> _ {
    // 初始化 Tera,將模板資料夾設為 "templates/**/*.html"
    let tera = Tera::new("templates/**/*.html").unwrap();

    // 構建 Rocket 伺服器,並註冊共享的 Tera 實例
    rocket::build()
        .manage(tera) // 將 Tera 實例註冊為共享狀態,供各處理器使用
        .mount("/", routes![index]) // 將 "/" 路徑掛載到 index 路由處理器
}

#[get("/")]
fn index(tera: &State<Tera>) -> RawHtml<String> {
    let mut context = Context::new(); // 創建一個 Tera Context實例為參數,用來傳遞變數到模板中

    // 添加變數到 Tera 的模板上下文中
    context.insert("name", "Rocket User"); // 在context實例中加入變數 "name" 的屬性,並將值設為 "Rocket User"

    // 渲染模板
    let rendered = tera.render("index.html", &context).unwrap(); // 使用 Tera 渲染 "index.html" 模板,並傳入 &context 作為參數

    // 返回渲染後的 HTML,包裹在 RawHtml 回應中
    RawHtml(rendered) // 返回渲染後的 HTML 結果
}

在這個範例中,我們使用了 Rocket 的 State 來管理 Tera 實例,並在每次請求時使用共享的 Tera 實例來渲染模板。我們將 index.html 模板中的變數 name 動態設為 "Rocket User",並通過 RawHtml 將渲染後的 HTML 內容返回給用戶。


4. 測試模板渲染

執行以下命令來啟動伺服器:

cargo run

接著,訪問 http://localhost:8000/,你會看到以下畫面:

Hello from Rocket using Tera!
Welcome, Rocket User!

實際畫面如下:
https://ithelp.ithome.com.tw/upload/images/20241008/2012117623XUy3hbMf.png

這表示我們已經成功將模板與 Rocket 結合,並能夠動態生成 HTML 頁面。


三、Rocket 的進階應用:表單處理

一般情況下, web service 除了要接收 GET request,還要能夠方便地處理 POST request,接收表單數據就是一個常見的例子。讓我們來看看如何使用 Rocket 處理一個簡單的表單提交。

1. 編寫模板來顯示表單數據

首先,我們需要編寫一個模板來顯示表單提交後的數據。請在 templates 資料夾中創建一個名為 form-example.html 的模板文件,內容如下:

<!-- templates/form-example.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Form Submission</title>
</head>
<body>
    <h1>Form Submission Result</h1>
    <p>Hello, {{ name }}! You are {{ age }} years old.</p>
</body>
</html>

此模板將動態顯示提交的 nameage 數據。


2. 修改 main.rs 來渲染模板並處理表單提交

接著,我們需要修改 main.rs 來處理表單提交並渲染 form-example.html 模板。這裡我們使用 Tera 來渲染表單結果。

#[macro_use]
extern crate rocket;

use rocket::form::Form;
use rocket::response::content::RawHtml;
use rocket::State;
use tera::{Context, Tera};

// 定義表單結構體
#[derive(FromForm)]
struct User {
    name: String,  // 使用者名稱
    age: u8,       // 使用者年齡
}

#[launch]
fn rocket() -> _ {
    // 初始化 Tera 模板引擎
    let tera = Tera::new("templates/**/*.html").unwrap();

    // 構建 Rocket 伺服器並註冊路由
    rocket::build()
        .manage(tera) // 將 Tera 實例設為共享狀態
        .mount("/", routes![index, submit]) // 掛載路由
}

// 定義一個處理 GET 請求的處理器,顯示表單
#[get("/")]
fn index() -> RawHtml<&'static str> {
    // 簡單的表單 HTML,提供表單輸入
    RawHtml(r#"
        <form action="/submit" method="post">
            <label for="name">Name:</label>
            <input type="text" id="name" name="name"><br><br>
            <label for="age">Age:</label>
            <input type="number" id="age" name="age"><br><br>
            <input type="submit" value="Submit">
        </form>
    "#)
}

// 定義一個處理 POST 請求的處理器,處理表單數據並渲染結果模板
#[post("/submit", data = "<user_form>")]
fn submit(user_form: Form<User>, tera: &State<Tera>) -> RawHtml<String> {
    let mut context = Context::new(); // 創建 Tera 上下文
    context.insert("name", &user_form.name); // 插入變數 "name"
    context.insert("age", &user_form.age.to_string()); // 插入變數 "age"

    // 渲染模板,將提交的數據顯示在表單結果頁面
    let rendered = tera.render("form-example.html", &context).unwrap();

    RawHtml(rendered) // 返回渲染後的 HTML
}

3. 執行結果

  1. 當 Rocket 伺服器啟動後,訪問 http://localhost:8000/,將會看到以下表單頁面:
<form action="/submit" method="post">
    <label for="name">Name:</label>
    <input type="text" id="name" name="name"><br><br>
    <label for="age">Age:</label>
    <input type="number" id="age" name="age"><br><br>
    <input type="submit" value="Submit">
</form>
  1. 當用戶填寫名稱和年齡並提交表單後,會看到表單結果被渲染在 form-example.html 中。假設輸入 name = "Alice"age = 30,則結果將會是:
Hello, Alice! You are 30 years old.

實際畫面如下:
https://ithelp.ithome.com.tw/upload/images/20241008/20121176HGJnQYvytM.png

這樣,我們成功地利用 RocketTera 模板引擎來渲染表單提交結果。


四、Rocket 與 Actix 的對比

我們已經學會如何使用 Rocket 建立一個簡單的 Web 伺服器,現在來對比 Rocket 和 Actix 這兩個框架的差異。

1. 同步 vs 非同步

Actix 是一個完全基於非同步的框架,這讓它在高併發情境下特別有優勢。而 Rocket 採用了同步模式,雖然目前的 Rocket 0.5 支援非同步函數,但其核心理念依然傾向於同步編程。這意味著:

  • Actix:非常適合高並發應用,能夠處理大量的同時連線,例如聊天應用、即時通知系統等。
  • Rocket:因為同步模式,相對簡單易用,更適合小型或中型應用,開發體驗更加直觀。

2. 編譯時 vs 運行時錯誤檢查

Rocket 擅長在編譯時進行靜態檢查,能夠在編譯時檢測大部分錯誤,尤其是路由參數和型別檢查。因此,Rocket 的開發過程中會有更少的運行時錯誤。

Actix 則更依賴運行時的錯誤處理,開發者在設計 API 時需要更加注意對異常情況的處理。

3. 學習曲線

  • Actix:由於 Actor 模型和非同步編程的特性,Actix 的學習曲線相對較陡,尤其是對於初學者來說,理解 Actor 模型可能需要更多時間。
  • Rocket:因為簡單直觀的同步模式和大量靜態檢查,Rocket 更加適合剛接觸 Rust 的開發者,學習難度較低。

4. 性能對比

從性能角度來看,Actix 通常在高併發情境下表現更好,它經常被用來處理需要極低延遲的應用場景,例如高頻 API 請求。Rocket 在小規模應用中性能也非常優秀,但當應用需要承擔大量並發請求時,Actix 的非同步特性會讓它更具優勢。

5. 生態系統與社群支持

Actix 擁有非常豐富的生態系統和插件,並且有大量的擴展功能,例如 WebSocket 支持、REST API 構建工具等。Rocket 的生態系統相對較小,但它的優點在於官方文檔非常詳盡,且框架設計精簡,開發起來更加輕量。

總結

首先總結一下Rocket套件的操作步驟:

Rocket 操作步驟:

  1. 建立專案與引入套件:使用 Cargo 建立新專案,並引入 Rocket 套件。
  2. 編寫路由與處理器:通過 Rocket 的語法,定義 GET 與 POST 路由,並編寫相應的處理器函數。
  3. 編譯並測試應用程式:使用 cargo run 啟動伺服器,通過瀏覽器訪問確認應用運行狀況。

在這篇文章中,我們介紹了 Rocket 框架的基本使用方法,並對比了它與 Actix 的差異。Actix 作為高併發環境下的首選框架,以其非同步支持和 Actor 模型聞名,而 Rocket 則以簡單直觀的同步編程模式和強大的編譯時錯誤檢查功能受到許多初學者與中型應用開發者的青睞。

看得出來,Rocket 適合作為新手學習使用,當然 Rocket 框架也已經能夠提供足夠的 web service 所應該具備的多項功能,下一篇文章我們將繼續探索如何使用 Rocket 建立 RESTful API 以及與資料庫互動溝通的方法。


上一篇
[Day 24] Rust 的 Web 應用(一):簡介 Actix 框架
下一篇
[Day 26] Rust 的 Web 應用(三):使用 Rocket 與 MongoDB 建立 RESTful API
系列文
從 Python 開發者的角度學習 Rust —— 從語法基礎到實戰應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言